
let Promise = function(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolved = []
    this.onRejected = []
    let resolve = value => {
        if (this.status === 'pending') {
            this.status = 'resolved'
            this.value = value
            this.onResolved.forEach(fn => fn())
        }
    }
    let reject = reason => {
        if (this.status === 'pending') {
            this.status = 'rejected'
            this.reason = reason
            this.onRejected.forEach(fn => fn())
        }
    }
    try {
        executor(resolve, reject)
    } catch (err) {
        reject(err)
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) { return reject(new TypeError('循环引用')) }
    let called
    if (x != null && (typeof x === 'function' || typeof x === 'object')) {
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, err => {
                    if (called) return
                    called = true
                    reject(err)
                })
            } else {
                if (called) return
                called = true
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            resolve(e)
        }
    } else {
        resolve(x)
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') onFulfilled = res => res
    if (typeof onRejected !== 'function') onRejected = err => {
        throw err
    }
    let promise2
    promise2 = new Promise((resolve, reject) => {
        if (this.status === 'resolved') {
            setTimeout(() => {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if (this.status === 'rejected') {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if (this.status === 'pending') {
            this.onResolved.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
            this.onRejected.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
    })
    return promise2
}

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected)
}

Promise.resolve = function (value) {
    return new Promise((resolve) => {
        resolve(value)
    })
}

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason)
    })
}

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let i = 0, len = promises.length
        let result = []
        function processData(index, data) {
            result[index] = data
            if (++i == len) {
                resolve(result)
            }
        }
        promises.forEach((promise, index) => {
            promise.then(res => {
                processData(index, res)
            }, reject)
        })
    })
}

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        promises.forEach(promise => {
            promise.then(resolve, reject)
        })
    })
}

